package base;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;


/**
 * Classe représentant une catégorie de tweets, c'est-à-dire un ensemble de tweets ayant une même polarité d'opinion. 
 * En l'occurrence, la catégorie pourra être "positive", "negative", "neutral", "irrelevant" ou "relevant".
 */
public class Category {

	/**
	 * Le nombre de tweets dans la catégorie
	 */
	private int nbTweets;
	
	/**
	 * Le nombre de mots différents dans la catégorie
	 */
	private int nbMotsDiff;
	
	/**
	 * Le nombre de mots total dans la catégorie
	 */
	private int nbMots;
	
	/**
	 * la longueur moyenne des messages de la catégorie, en nombre de mots
	 */
	private int messageLong;

	/**
	 * La liste des mots de la catégorie, avec les autres informations, contenues dans une chaîne de caractères : 
	 * - le nombre de tweets qui contiennent le mot,
	 * - le nombre d'occurrences du mot,
	 * - le bêta pour le modèle Bernoulli,
	 * - le bêta pour le modèle multinomial
	 */
	private HashMap<String, Mot> mots;

	
	/**
	 * Constructeur d'un objet Category, 
	 * Initialise les attributs à zéro et crée une liste vide de mots
	 */
	public Category() {
		this.nbTweets = 0;
		this.nbMotsDiff = 0;
		this.nbMots = 0;
		this.mots = new HashMap<String, Mot>();
	}

	/**
	 * Renvoie le nombre de documents dans la catégorie
	 * @return le nombre de documents
	 */
	public int getNbDoc() {
		return this.nbTweets;
	}

	/**
	 * Renvoie le nombre de mots différents dans la catégorie
	 * @return le nombre de mots différents
	 */
	public int getNbMotsDiff() {
		return this.nbMotsDiff;
	}

	/**
	 * Renvoie le nombre de mots dans la catégorie
	 * @return le nombre de mots
	 */
	public int getNbMots() {
		return this.nbMots;
	}
	
	/**
	 * Renvoie la longueur moyenne des messages de la catégorie, en nombre de mots
	 * @return la longueur moyenne des messages
	 */
	public int getMessageLong() {
		return this.messageLong;
	}

	/**
	 * Renvoie le nombre de tweets qui contiennent un mot donné en paramètre
	 * @param mot le mot
	 * @return le nombre de tweets qui contiennent le mot
	 */
	public int getNbDocMot(String mot) {
		Mot motClass = mots.get(mot);
		if (motClass == null)
			return 0;

		return motClass.getNbTweets();
	}

	/**
	 * Renvoie le nombre d'occurences d'un mot donné en paramètre
	 * @param mot le mot
	 * @return le nombre d'occurences du mot
	 */
	public int getNbOccMot(String mot) {
		Mot motClass = mots.get(mot);
		if (motClass == null)
			return 0;

		return motClass.getNbOcc();
	}
	
	/**
	 * Renvoie les n mots les plus fréquents dans la catégorie
	 * @param n le nombre de mots
	 * @return la liste des n mots les plus fréquents
	 */
	public ArrayList<String> getMotsFrequents(int n) {
		ArrayList<String> keys = new ArrayList<String>(mots.keySet());

		Collections.sort(keys, new Comparator<Object>() {
			public int compare(Object o1, Object o2) {
				if (mots.get(o1).getNbOcc() < mots.get(o2).getNbOcc())
					return 1;

				else if (mots.get(o1).getNbOcc() == mots.get(o2).getNbOcc())
					return 0;

				else
					return -1;
			}
		});

		ArrayList<String> motList = new ArrayList<String>();

		for (int i = 0; i < keys.size() && i < n; i++) {
			motList.add(keys.get(i));
		}

		return motList;
	}

	/**
	 * Renvoie les n mots les moins fréquents dans la catégorie
	 * @param n le nombre de mots
	 * @return la liste des n mots les moins fréquents
	 */
	public ArrayList<String> getMotsRares(int n) {
		ArrayList<String> keys = new ArrayList<String>(mots.keySet());

		Collections.sort(keys, new Comparator<Object>() {
			public int compare(Object o1, Object o2) {
				if (mots.get(o1).getNbOcc() > mots.get(o2).getNbOcc())
					return 1;

				else if (mots.get(o1).getNbOcc() == mots.get(o2).getNbOcc())
					return 0;

				else
					return -1;
			}
		});

		ArrayList<String> motList = new ArrayList<String>();

		for (int i = 0; i < keys.size() && i < n; i++) {
			motList.add(keys.get(i));
		}

		return motList;
	}

	/**
	 * Renvoie les n mots les plus significatifs dans la catégorie
	 * @param n le nombre de mots
	 * @return la liste des n mots les plus significatifs
	 */
	public ArrayList<String> getMotsPlusSignificatifs(int n) {
		ArrayList<String> keys = new ArrayList<String>(mots.keySet());

		Collections.sort(keys, new Comparator<Object>() {
			public int compare(Object o1, Object o2) {
				if (mots.get(o1).getInfoMutuelle() < mots.get(o2)
						.getInfoMutuelle())
					return 1;

				else if (mots.get(o1).getInfoMutuelle() == mots.get(o2)
						.getInfoMutuelle())
					return 0;

				else
					return -1;
			}
		});

		ArrayList<String> motList = new ArrayList<String>();

		for (int i = 0; i < keys.size() && i < n; i++) {
			motList.add(keys.get(i));
		}

		return motList;
	}

	/**
	 * Renvoie les n mots les moins significatifs dans la catégorie
	 * @param n le nombre de mots
	 * @return la liste des n mots les moins significatifs
	 */
	public ArrayList<String> getMotsMoinsSignificatifs(int n) {
		ArrayList<String> keys = new ArrayList<String>(mots.keySet());

		Collections.sort(keys, new Comparator<Object>() {
			public int compare(Object o1, Object o2) {
				if (mots.get(o1).getInfoMutuelle() > mots.get(o2)
						.getInfoMutuelle())
					return 1;

				else if (mots.get(o1).getInfoMutuelle() == mots.get(o2)
						.getInfoMutuelle())
					return 0;

				else
					return -1;
			}
		});

		ArrayList<String> motList = new ArrayList<String>();

		for (int i = 0; i < keys.size() && i < n; i++) {
			motList.add(keys.get(i));
		}

		return motList;
	}

	/**
	 * Traite un tweet de la catégorie afin d'ajouter ses mots à la liste des mots de la catégorie
	 * @param listMots la liste des mots du tweet
	 */
	public void traiterUnTweet(ArrayList<String> listMots) {

		ArrayList<String> motsTemp = new ArrayList<String>();
		int nbOccurence;

		this.nbTweets++;

		for (String motTemp : listMots) {
			if (motTemp.compareTo("") == 0)
				continue;

			this.nbMots++;

			if (mots.containsKey(motTemp)) {
				nbOccurence = mots.get(motTemp).getNbOcc();
				nbOccurence++;
				mots.get(motTemp).setNbOcc(nbOccurence);

				if (!motsTemp.contains(motTemp)) {
					motsTemp.add(motTemp);
					nbOccurence = mots.get(motTemp).getNbTweets();
					nbOccurence++;
					mots.get(motTemp).setNbTweets(nbOccurence);
				}
			}
			else {
				motsTemp.add(motTemp);
				this.nbMotsDiff++;
				Mot mot1 = new Mot();
				mot1.setNbTweets(1);
				mot1.setNbOcc(1);
				mots.put(motTemp, mot1);
			}
		}
	}

	/**
	 * Calcule l'information mutuelle et la longueur moyenne des messages des tweets de la catégorie
	 * @param categories la liste des catégories
	 * @param nbMessageTotal le nombre total de messages
	 */
	public void finirTraitement(ArrayList<Category> categories, int nbMessageTotal) {
		this.messageLong = (int) (this.nbMots / this.nbTweets);
		this.nbMotsDiff = mots.size();
		
		for (String motTemp : mots.keySet()) {
			int nbDocMot = mots.get(motTemp).getNbTweets();
			
			int nbDocTotal = 0;
			
			for(int i = 0 ; i < categories.size(); i++)
				nbDocTotal+= categories.get(i).getNbDocMot(motTemp);

			double infoMutuelle = Math.log(nbMessageTotal * nbDocMot
					/ (nbDocTotal * this.nbTweets));
			
			mots.get(motTemp).setInfoMutuelle(infoMutuelle);
		}
	}
}
